﻿namespace MyTRCP.Common
{
    public class BitWalker
    {
        private readonly List<byte> _data;
        private readonly bool _fixedLen;
        private int _pos;
        private int BitLen
        {
            get
            {
                return _data.Count * 8;
            }
        }
        public int ByteLen
        {
            get
            {
                return _data.Count;
            }
        }
        public int RemainingByteLen
        {
            get
            {
                return RemainingBitLen / 8;
            }
        }
        public int RemainingBitLen
        {
            get
            {
                return BitLen - _pos;
            }
        }
        public BitWalker()
        {
            _data = [];
            _fixedLen = false;
        }
        public BitWalker(byte[] data, bool fixedLen = true) : this(data, 0, data.Length, fixedLen) { }
        public BitWalker(byte[] data, int pos, int len, bool fixedLen = true)
        {
            int maxLen = data.Length - pos;
            if (len > maxLen)
            {
                len = maxLen;
            }
            _data = new List<byte>();
            for (int i = pos; i < pos + len; i++)
            {
                _data.Add(data[i]);
            }
            _fixedLen = fixedLen;
        }
        public int Pos
        {
            get
            {
                return _pos;
            }
            set
            {
                if (_fixedLen && value > BitLen)
                    throw new ArgumentOutOfRangeException(nameof(value), "位置不能超过总长度");
                if (value < 0)
                    throw new ArgumentOutOfRangeException(nameof(value), "位置不能小于0");
                _pos = value;
            }
        }
        public T ReadBitWalkableObj<T>() where T : BitWalkableObj, new()
        {
            T obj = new T();
            obj.ReadFromBitWalker(this);
            return obj;
        }
        public IReadOnlyCollection<T> ReadBitWalkableObjs<T>(int count) where T : BitWalkableObj, new()
        {
            List<T> objs = new List<T>();
            for (int i = 0; i < count; i++)
            {
                objs.Add(ReadBitWalkableObj<T>());
            }
            return objs.AsReadOnly();
        }
        public BitWalker WriteBitWalkableObj(BitWalkableObj obj)
        {
            obj.WriteToBitWalker(this);
            return this;
        }
        public BitWalker WriteBitWalkableObjs(IEnumerable<BitWalkableObj> objs)
        {
            foreach (BitWalkableObj obj in objs)
            {
                WriteBitWalkableObj(obj);
            }
            return this;
        }
        public BitWalker WriteBytes(byte[] data)
        {
            ArgumentNullException.ThrowIfNull(data, nameof(data));
            for (int i = 0; i < data.Length; i++)
            {
                WriteBits(data[i], 8);
            }
            return this;
        }
        public BitWalker WriteBits(int data, int bits)
        {
            if (bits <= 0)
                throw new ArgumentOutOfRangeException(nameof(bits), "读取位数不能小于0");
            if (bits > 32)
                throw new ArgumentOutOfRangeException(nameof(bits), "一次读取不能超过32位");
            if (_fixedLen && Pos + bits > BitLen)
                throw new ArgumentOutOfRangeException(nameof(bits), "要写的位数会超过总位数");
            uint temp = (uint)data << (32 - bits);
            for (int b = 0; b < bits; b++)
            {
                SetNowPosBit((temp >> 31) != 0);
                Pos++;
                temp <<= 1;
            }
            return this;
        }
        public BitWalker WriteBit(bool b)
        {
            WriteBits(b ? 1 : 0, 1);
            return this;
        }
        public byte[] ReadBytesToEnd()
        {
            int totalLen = BitLen - Pos;
            int intLen = totalLen / 8;
            int d = totalLen % 8;
            if (d > 0)
                intLen++;
            return ReadBytes(intLen);
        }
        public byte[] ReadBytes(int byteLen)
        {
            if (byteLen < 0)
                throw new ArgumentOutOfRangeException(nameof(byteLen), "读取字节数数不能小于0");
            if (_fixedLen && Pos + byteLen * 8 > BitLen)
                throw new ArgumentOutOfRangeException(nameof(byteLen), "要读的位数会超过总位数");
            byte[] bytes = new byte[byteLen];
            for (int i = 0; i < byteLen; i++)
            {
                bytes[i] = (byte)ReadBits(8);
            }
            return bytes;
        }
        public uint ReadBits(int bits)
        {
            if (bits <= 0)
                throw new ArgumentOutOfRangeException(nameof(bits), "读取位数不能小于0");
            if (bits > 32)
                throw new ArgumentOutOfRangeException(nameof(bits), "一次读取不能超过32位");
            if (_fixedLen && Pos + bits > BitLen)
                throw new ArgumentOutOfRangeException(nameof(bits), "要读的位数会超过总位数");
            uint result = 0;
            for (int b = 0; b < bits; b++)
            {
                result <<= 1;
                if (GetNowPosBit())
                {
                    result |= 1;
                }
                Pos++;
            }
            return result;
        }
        public bool ReadBit()
        {
            return ReadBits(1) != 0;
        }
        public BitWalker SetPosToZero()
        {
            Pos = 0;
            return this;
        }
        public BitWalker SetPos(int pos)
        {
            Pos = pos;
            return this;
        }
        public BitWalker SkipBits(int bits)
        {
            if (_fixedLen && Pos + bits > BitLen)
                throw new ArgumentOutOfRangeException(nameof(bits), "要读的位数会超过总位数");
            Pos += bits;
            return this;
        }
        private bool GetNowPosBit()
        {
            if (!_fixedLen && Pos >= BitLen)
            {
                return false;
            }
            return ((_data[Pos / 8] >> (7 - (Pos % 8))) & 1) != 0;
        }
        private void SetNowPosBit(bool b)
        {
            if (!_fixedLen)
            {
                while (_pos >= BitLen)
                {
                    _data.Add(0);
                }
            }
            int byteIndex = Pos / 8;
            int bitIndex = 7 - (Pos % 8);
            if (b)
            {
                _data[byteIndex] |= (byte)(1 << bitIndex);
            }
            else
            {
                _data[byteIndex] &= (byte)~(1 << bitIndex);
            }
        }
        public byte[] ToBytes()
        {
            return [.. _data];
        }
    }
}